package oneD.fem.visualize;

import inf.jlinalg.Vector3D;
import inf.v3d.obj.Arrow;
import inf.v3d.obj.Cone;
import inf.v3d.obj.Text;
import inf.v3d.view.Viewer;

import java.util.ArrayList;

import oneD.dynamics.DynamicStructure;
import oneD.fem.Node;
import oneD.fem.structure.Structure;


public class Visualizer {
	protected double displacementScale_;
	// private double symbolScale_;
	private double constraintSymbolScale_;
	private double forceSymbolRadius_;
	private double forceSymbolScale_;
	private double linePlotScale_;
	protected double textHeight_;
	protected Vector3D centerOfNodeGravity_;

	protected Structure struct_;
	protected Viewer view_;

	public Visualizer(Structure struct, Viewer view) {
		this.struct_ = struct;
		this.view_ = view;
	}

	public void drawElements() {

	}

	public void drawConstraints() {
		for (int i = 0; i < this.struct_.getNumberOfNodes(); i++) {
			if (this.struct_.getNode(i).getConstraint() != null) {
				if (!this.struct_.getNode(i).getConstraint().isFree(0)) {
					Cone c = new Cone();
					c.setRadius(this.constraintSymbolScale_);
					c.setHeight(this.constraintSymbolScale_);
					c.setColor("blue");
					c.setDirection(1, 0, 0);
					c.setCenter(this.struct_.getNode(i).getPosition().getX1()
							- this.constraintSymbolScale_, this.struct_
							.getNode(i).getPosition().getX2(), this.struct_
							.getNode(i).getPosition().getX3());
					this.view_.addObject3D(c);
				}
				if (!this.struct_.getNode(i).getConstraint().isFree(1)) {
					Cone c = new Cone();
					c.setRadius(this.constraintSymbolScale_);
					c.setHeight(this.constraintSymbolScale_);
					c.setColor("blue");
					c.setDirection(0, 1, 0);
					c.setCenter(this.struct_.getNode(i).getPosition().getX1(),
							this.struct_.getNode(i).getPosition().getX2()
									- this.constraintSymbolScale_, this.struct_
									.getNode(i).getPosition().getX3());
					this.view_.addObject3D(c);
				}
				if (!this.struct_.getNode(i).getConstraint().isFree(2)) {
					Cone c = new Cone();
					c.setRadius(this.constraintSymbolScale_);
					c.setHeight(this.constraintSymbolScale_);
					c.setColor("blue");
					c.setDirection(0, 0, 1);
					c.setCenter(this.struct_.getNode(i).getPosition().getX1(),
							this.struct_.getNode(i).getPosition().getX2(),
							this.struct_.getNode(i).getPosition().getX3()
									- this.constraintSymbolScale_);
					this.view_.addObject3D(c);
				}
			}
		}
	}

	public void drawForces() {
		for (int i = 0; i < this.struct_.getNumberOfNodes(); i++) {
			Arrow a = new Arrow();
			double[] start = this.struct_
					.getNode(i)
					.getPosition()
					.subtract(
							this.struct_.getNode(i).getForceVector()
									.multiply(this.forceSymbolScale_))
					.toArray();
			a.setPoint1(start);
			a.setPoint2(this.struct_.getNode(i).getPosition().toArray());
			a.setColor("red");
			a.setRadius(this.forceSymbolRadius_);
			this.view_.addObject3D(a);
		}
	}

	public void drawElementForces() {
	}

	public void drawDisplacements() {
	}

	public void drawDisplacedStructure() {

	}

	public void setConstraintSymbolScale(double d) {
		this.constraintSymbolScale_ = d;
	}

	public void setForceSymbolRadius(double d) {
		this.forceSymbolRadius_ = d;
	}

	public void setDisplacementScale(double d) {
		this.displacementScale_ = d;
	}

	public void setForceSymbolScale(double d) {
		this.forceSymbolScale_ = d;
	}

	public void setLinePlotScale(double d) {
		this.linePlotScale_ = d;
	}

	public void setTextHeightScale(double d) {
		this.textHeight_ = d;
	}

	public double automaticScale() {
		// setting the relative scale of the visual components
		// finding maximum distance between 2 points
		double d_max = 0;
		for (int i = 0; i < this.struct_.getNumberOfNodes(); i++)
			for (int j = i + 1; j < this.struct_.getNumberOfNodes(); j++) {
				double d = this.computeDistance(this.struct_.getNode(i),
						this.struct_.getNode(j));
				if (d_max < d)
					d_max = d;
			}

		// finding f_max
		double f_max = 0;
		for (int i = 0; i < this.struct_.getNumberOfNodes(); i++) {
			double f = this.struct_.getNode(i).getForceVector().norm2();
			if (f_max < f)
				f_max = f;
		}

		this.setForceSymbolScale(0.3 * d_max / f_max);
		this.setForceSymbolRadius(this.forceSymbolScale_ * f_max / 50);
		this.setConstraintSymbolScale(d_max / 20);

		this.setTextHeightScale(d_max / 50);

		// compute center of gravity of node (for text presentation later on)
		// this.centerOfNodeGravity_ = new Vector3D(0, 0, 0);
		// for (int i = 0; i < this.struct_.getNumberOfNodes(); i++)
		// this.centerOfNodeGravity_ = this.centerOfNodeGravity_
		// .add(this.struct_.getNode(i).getPosition());
		// this.centerOfNodeGravity_ = this.centerOfNodeGravity_.multiply(1 /
		// 3);

		return d_max;
	}

	public double postAutomaticScale(double d_max) {
		// finding u_max
		double u_max = 0;
		for (int i = 0; i < this.struct_.getNumberOfNodes(); i++) {
			double u = this.struct_.getNode(i).getDisplacement().norm2();
			if (u_max < u)
				u_max = u;
		}
		this.setDisplacementScale(0.2 * d_max / u_max);
		return u_max;
	}

	public double dynamicAutomaticScale(DynamicStructure dStruct, double d_max) {
		// finding u_max
		double u_max = 0;
		for (int i = 0; i < this.struct_.getNumberOfNodes(); i++)
			for (int j = 0; j < dStruct.getNumberOfStep(); j++) {
				double u = dStruct.getNode(i).getDisplacement(j).norm2();
				if (u_max < u)
					u_max = u;
			}
		this.setDisplacementScale(0.2 * d_max / u_max);
		return u_max;
	}

	private double computeDistance(Node n1, Node n2) {
		return n2.getPosition().subtract(n1.getPosition()).norm2();
	}

	public void showNodeId() {
		for (int i = 0; i < this.struct_.getNumberOfNodes(); i++) {
			Text t = new Text(Integer.toString(i));
			double[] pos = this.struct_.getNode(i).getPosition().toArray();
			t.setOrigin(pos[0] + this.textHeight_, pos[1] + this.textHeight_,
					pos[2] + this.textHeight_);
			t.setHeight(this.textHeight_);
			t.setColor("magenta");
			// double rx = pos[0] - this.centerOfNodeGravity_.getX1();
			// double ry = pos[1] - this.centerOfNodeGravity_.getX2();
			// double rz = pos[2] - this.centerOfNodeGravity_.getX3();
			// t.setOrientation(rx, ry, rz);
			this.view_.addObject3D(t);
		}
	}

	public void showElementId() {

	}

	public void showElementGroupId(ArrayList<Integer> elementGroupId) {

	}

	public void drawElements(ArrayList<Integer> index, String color) {

	}

	public boolean advance() {
		/*
		 * use to advance step in dynamic visualizer
		 * return true if advance to last step
		 */
		return true;
	}

	public boolean retreat() {
		/*
		 * use to retreat step in dynamic visualizer
		 * return true if retreat to initial step
		 */
		return true;
	}

}
